package br.ufpe.cin.mac.util;

import java.io.IOException;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import br.ufpe.cin.mac.middleware.autonomic.Checker;
import br.ufpe.cin.mac.middleware.autonomic.Config;
import br.ufpe.cin.mac.middleware.autonomic.KnowledgeBase;
import br.ufpe.cin.mac.middleware.autonomic.Transition;
import br.ufpe.cin.mac.middleware.autonomic.monitors.MemoryMonitor;
import br.ufpe.cin.mac.middleware.autonomic.monitors.Monitor;
import br.ufpe.cin.mac.middleware.autonomic.monitors.NetworkMonitor;
import br.ufpe.cin.mac.middleware.autonomic.monitors.ProcessorMonitor;
import br.ufpe.cin.mac.middleware.components.concrete.Crypto;
import br.ufpe.cin.mac.middleware.components.concrete.Decrypt;
import br.ufpe.cin.mac.middleware.components.concrete.GZIP;
import br.ufpe.cin.mac.middleware.components.concrete.Jms;
import br.ufpe.cin.mac.middleware.components.concrete.Manager;
import br.ufpe.cin.mac.middleware.components.concrete.Security.Security;
import br.ufpe.cin.mac.middleware.connectors.abstracts.Mom;
import br.ufpe.cin.mac.middleware.connectors.abstracts.Reliable;
import br.ufpe.cin.mac.middleware.connectors.concrete.HttpClient;
import br.ufpe.cin.mac.middleware.connectors.concrete.HttpServer;
import br.ufpe.cin.mac.middleware.connectors.concrete.RTPClient;
import br.ufpe.cin.mac.middleware.connectors.concrete.RTPServer;
import br.ufpe.cin.mac.middleware.connectors.concrete.TcpClient;
import br.ufpe.cin.mac.middleware.connectors.concrete.TcpServer;
import br.ufpe.cin.mac.middleware.connectors.concrete.UDPClient;
import br.ufpe.cin.mac.middleware.connectors.concrete.UDPServer;
import br.ufpe.cin.mac.middleware.connectors.metaconnector.Configurator;
import br.ufpe.cin.mac.parser.Binds;
import br.ufpe.cin.mac.parser.CanonicalElements;
import br.ufpe.cin.mac.parser.XBind;
import br.ufpe.cin.mac.parser.XComponent;
import br.ufpe.cin.mac.parser.XConnector;
import br.ufpe.cin.mac.parser.XMiddleware;
import br.ufpe.cin.mac.parser.xml.XmlReader;

public class Loader {

	public static Object getComponent(String id) {
		if (id.equals(Constants.COMP_CRYPT)) {
			return new Crypto();
		} else if (id.equals(Constants.COMP_DECRYPT))
			return new Decrypt();

		else if (id.equals(Constants.COMP_JMS))
			return new Jms();

		else if (id.equals(Constants.COMP_SECURITY))
			return new Security();

		else if (id.equals(Constants.COMP_MANAGER))
			return new Manager();

		else if (id.equals(Constants.COMP_CHECKER))
			return new Checker(Loader.loadKnowledgeBase());

		else if (id.equals(Constants.COMP_GZIP))
			return new GZIP();
		else
			return null;
	}

	public static Object getConcreteConnector(String id) {

		if (id.equals(Constants.CONN_HTTPCLIENT))
			return new HttpClient();

		else if (id.equals(Constants.CONN_HTTPSERVER))
			return new HttpServer();

		else if (id.equals(Constants.CONN_TCPSERVER))
			return new TcpServer();

		else if (id.equals(Constants.CONN_TCPCLIENT))
			return new TcpClient();
		else if (id.equals(Constants.CONN_RTPSERVER))
			return new RTPServer();
		else if (id.equals(Constants.CONN_RTPCLIENT))
			return new RTPClient();
		else if (id.equals(Constants.CONN_UDPCLIENT))
			return new UDPClient();
		else if (id.equals(Constants.CONN_UDPSERVER))
			return new UDPServer();
		else
			return null;
	}

	public static Object getConnector(String id, String compFrom,
			String compTo, String connConcrete) {
		if (Constants.CONN_MOM.equals(id))
			return new Mom(compFrom, compTo, connConcrete);
		else if (Constants.CONN_RELIABLE.equals(id))
			return new Reliable(compFrom, compTo, connConcrete);
		else
			return null;
	}

	/*
	 * Os m�dulos de comunica��o escutam o Manager O Manager ouve todos os
	 * demais
	 */

	public static MBeanServer loader(MBeanServer mb, String confFile) {
		try {
			ObjectName objName = null;
			ObjectName metaConnec = null;
			// Cria��o dos elementos de leitura do XML
			XmlReader xml = new XmlReader();
			XMiddleware xmid = xml.readXml(confFile);

			// Leitura do arquivo confFile, obten��o dos Binds
			Binds binds = xmid.getBinds();
			CanonicalElements elements = xmid.getCanonicalElements();

			XComponent compFrom = null;
			XComponent compTo = null;
			XConnector conn = null;
			XConnector concreteConnector = null;

			Iterator<XBind> it = binds.getBinds().iterator();
			// Realiza a leitura do Bind TODO sao um Bind
			while (it.hasNext()) {
				XBind xbind = it.next();
				// Obtem o primeiro componente

				compFrom = elements.getXComponent(xbind.getFrom());
				// Obtem o segundo componente
				compTo = elements.getXComponent(xbind.getTo());
				// Obtem o connector abstrato
				conn = elements.getXConnector(xbind.getThrough());

				// Obtem o connector concreto
				concreteConnector = elements.getConcreteConnector();

				/*
				 * Iniciando registro dos elementos no JMX
				 */

				// Configurador
				// Registro do configurador
				metaConnec = new ObjectName(
						"Middleware:Type=MetaConnector,name=Configurator");
				mb
						.registerMBean(new Configurator(mb, conn.getId()),
								metaConnec);

				Object obj = getComponent(compFrom.getId());
				objName = new ObjectName(compFrom.getId());
				mb.registerMBean(obj, objName);
				mb.addNotificationListener(objName, metaConnec, null, null);

				obj = getComponent(compTo.getId());
				objName = new ObjectName(compTo.getId());
				mb.registerMBean(obj, objName);
				mb.addNotificationListener(objName, metaConnec, null, null);

				obj = getConcreteConnector(concreteConnector.getId());
				objName = new ObjectName(concreteConnector.getId());
				mb.registerMBean(obj, objName);
				mb.addNotificationListener(objName, metaConnec, null, null);

				obj = getConnector(conn.getId(), compFrom.getId(), compTo
						.getId(), concreteConnector.getId());
				objName = new ObjectName(conn.getId());
				mb.registerMBean(obj, objName);
				mb.addNotificationListener(objName, metaConnec, null, null);

			}
			try {
				if (xmid.getType().equals(Constants.TYPE_CLIENT)) {
					objName = new ObjectName(Constants.COMP_CHECKER);
					Checker ch = (Checker) getComponent(Constants.COMP_CHECKER);
					loadMonitors(ch);
					mb.registerMBean(ch, objName);
					mb.addNotificationListener(objName, metaConnec, null, null);

					// Object obj =
					// getConcreteConnector(Constants.CONN_TCPCLIENT);
					// objName = new ObjectName(Constants.CONN_TCPCLIENT);
					// / mb.registerMBean(obj, objName);
					// mb.addNotificationListener(objName, metaConnec, null,
					// null);
				} else {

					try {

						Object obj = getConcreteConnector(Constants.CONN_TCPSERVER);
						objName = new ObjectName(Constants.CONN_TCPSERVER);
						mb.registerMBean(obj, objName);
						mb.addNotificationListener(objName, metaConnec, null,
								null);
					} catch (Exception e) {
						e.printStackTrace();
					}

				}
				objName = new ObjectName(Constants.COMP_DECRYPT);
				mb.registerMBean(new Decrypt(), objName);
				mb.addNotificationListener(objName, metaConnec, null, null);

			} catch (Exception e) {
				e.printStackTrace();
				System.err.println("Error during Checker loading");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("MBeanServer Loaded");
		return mb;
	}

	public static KnowledgeBase loadKnowledgeBase() {
		// REGRAS 1
		Transition t1 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				4, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_GZIP, Constants.CONN_UDPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.60f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 4, 18, 40 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.85f, 0.10f, 0.50f }, Constants.RULE_NUMBER1);
		Transition t2 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_GZIP, Constants.CONN_UDPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 30 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.95f, 0.10f, 0.50f }, Constants.RULE_NUMBER1);
		Transition t3 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_GZIP, Constants.CONN_UDPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER1);

		// REGRAS 2
		Transition t4 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_SECURITY,
				Constants.COMP_GZIP, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER2);
		Transition t5 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_SECURITY,
				Constants.COMP_GZIP, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER2);
		Transition t6 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_SECURITY,
				Constants.COMP_GZIP, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_SECURITY, Constants.COMP_GZIP,
						Constants.CONN_UDPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER2);
		// REGRAS 3
		Transition t7 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_SECURITY, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_CRYPT, Constants.COMP_GZIP,
						Constants.CONN_TCPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER3);
		Transition t8 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_SECURITY, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_CRYPT, Constants.COMP_GZIP,
						Constants.CONN_TCPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER3);
		Transition t9 = Loader.getGeneratedTransition(new Integer[] { 2008, 6,
				5, 18, 39 }, new String[] { Constants.COMP_CRYPT,
				Constants.COMP_SECURITY, Constants.CONN_TCPCLIENT,
				Constants.CONN_RELIABLE }, new Float[] { 0.48f, 0.30f, 0.50f },
				new Integer[] { 2008, 6, 5, 18, 40 }, new String[] {
						Constants.COMP_CRYPT, Constants.COMP_GZIP,
						Constants.CONN_TCPCLIENT, Constants.CONN_RELIABLE },
				new Float[] { 0.48f, 0.10f, 0.50f }, Constants.RULE_NUMBER3);

		KnowledgeBase kb = new KnowledgeBase();
		kb.addTransition(t1);
		kb.addTransition(t2);
		kb.addTransition(t3);

		kb.addTransition(t4);
		kb.addTransition(t5);
		kb.addTransition(t6);

		kb.addTransition(t7);
		kb.addTransition(t8);
		kb.addTransition(t9);
		return kb;
	}

	public static Transition getGeneratedTransition(Integer[] beforeCalendar,
			String[] strBeforeInter, Float[] beforeMonitors,
			Integer[] afterCalendar, String[] strAfterInter,
			Float[] afterMonitors, String rule) {
		Config before;
		Calendar beforeCal = Calendar.getInstance();
		List<String> beforeInterceptors;
		beforeInterceptors = new LinkedList<String>();
		for (int i = 0; i < strBeforeInter.length; i++) {
			beforeInterceptors.add(strBeforeInter[i]);
		}

		before = new Config();
		beforeCal.set(beforeCalendar[0], beforeCalendar[1], beforeCalendar[2],
				beforeCalendar[3], beforeCalendar[4]);
		before.setCalendar(beforeCal);
		before.setInterceptors(beforeInterceptors);
		before.putValue(Constants.MON_Memory, beforeMonitors[0]);
		before.putValue(Constants.MON_Processor, beforeMonitors[1]);
		before.putValue(Constants.MON_Throughput, beforeMonitors[2]);

		Config after;
		Calendar afterCal = Calendar.getInstance();
		List<String> afterInterceptors;
		afterInterceptors = new LinkedList<String>();
		for (int i = 0; i < strAfterInter.length; i++) {
			afterInterceptors.add(strAfterInter[i]);
		}

		after = new Config();
		afterCal.set(afterCalendar[0], afterCalendar[1], afterCalendar[2],
				afterCalendar[3], afterCalendar[4]);
		after.setCalendar(afterCal);
		after.setInterceptors(afterInterceptors);
		after.putValue(Constants.MON_Memory, afterMonitors[0]);
		after.putValue(Constants.MON_Processor, afterMonitors[1]);
		after.putValue(Constants.MON_Throughput, afterMonitors[2]);

		Transition t = new Transition(before, after, rule);

		return t;
	}

	public static void loadMonitors(Checker ch) {

		try {
			Monitor memory = new MemoryMonitor();
			Monitor network = new NetworkMonitor();
			Monitor processor = new ProcessorMonitor();

			ch.addMonitor(Constants.MON_Memory, memory);
			ch.addMonitor(Constants.MON_Throughput, network);
			ch.addMonitor(Constants.MON_Processor, processor);

		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
